import { extend, deg2Rad, getBoundRect, lngLatToWorld, transformCoordinate, unTransformCoordinate, worldToLngLat, mix, isFunction } from './util'
import Stats from './Stats'
import { EARTH_RADIUS, ELEVATION } from './Constant'
import EarthControl from './EarthControl'
import AnimationManager from './AnimationManager'
import WorkerQueue from './WorkerQueue'
import { TimelineLite, Linear, Power4 } from 'gsap'
import axios from 'axios'

import Skybox from './EntitySkybox'
import Ground from './EntityGround'
import Building from './EntityBuilding'
import River from './EntityRiver'
import Landuse from './EntityLanduse'
import AimPulse from './EntityAimPulse'
import Shield from './EntityShield'
import Road from './EntityRoad'
import Text from './EntityText'
import Marker from './EntityMarker'

import keymap from '@/assets/jsons/keymap'

// 三维地图对象类
export default class SchoolLab {
  constructor(options) {
    // 合并设置
    const opts = extend({
      id: 'school-lab__container',
      name: 'schoolLab',
      debugger: true,
      center: [120, 30],
      config: {
        dBearing: 180,
        dPitch: 0,
        dHeight: 1000,
        bearing: 0,
        pitch: 45,
        height: 500
      },
      style: {}
    }, options)
    // 是否为调试模式
    this.debugger = opts.debugger
    // 调试模式打印配置
    this.debugger && console.log('schoolLab Opts：', opts)

    // 当前容器id
    this.id = opts.id
    // 当前组件name参数
    this.name = opts.name
    // 默认场景中心经纬度坐标
    this.center = opts.center
    // 基础配置
    this.config = opts.config
    // 组件样式配置
    this.style = opts.style
    // 当前场景中心的世界坐标
    this.gisCenter = lngLatToWorld(this.center)
    // 楼层选中回调函数
    this.storeyChooseCb = opts.storeyChooseCb
    // 楼层取消选中回调函数
    this.storeyUnChooseCb = opts.storeyUnChooseCb
    // 气泡点击事件回调
    this.popupClickCb = opts.popupClickCb

    // 三维地图dom容器
    this.box = document.getElementById(this.id)

    // three场景基础元素
    // 性能调试器
    this.stats = null
    // 时钟
    this.clock = null
    // 三维场景
    this.scene = null
    // 三维相机
    this.camera = null
    // 射线 用于判断点击以及hover事件
    this.raycaster = null
    // 场景渲染器
    this.renderer = null
    // 控制器
    this.control = null
    // 参考坐标系
    this.axes = null
    // 环境光
    this.ambientLight = null
    // 场景平行光
    this.directionalLights = []
    // 当前hover的entity对象
    this._hoverEntity = null

    // 天空盒子
    this.skybox = null
    // 地面
    this.ground = null
    // 河流对象集合
    this.rivers = []
    // 绿地对象
    this.landuse = null
    // 选中楼下方的扩散标识
    this.aimPulse = null
    // 楼层展示的外包围框
    this.shield = null
    // 楼层展示顶部的title标题
    this.storeyTitle = null
    // 场景内所有的building对象
    this.buildings = {}
    // this.buildings = []
    // 道路对象
    this.roads = []
    // 当前展示的楼层对象
    this.storeys = []
    // 气泡对象集合
    this.markers = {}

    // 当前鼠标屏幕坐标
    this.mousePos = new THREE.Vector2()
    // 动画管理器
    this.animationManager = new AnimationManager()
    // worker执行队列
    this.workerQueue = new WorkerQueue()
    // 注册主动画
    this.animationManager.regist('render', this._render.bind(this))

    // 当前是否支持hover事件
    this.enableHover = false
    // 当前是否支持点击事件
    this.enableClick = false

    // 是否支持自动场景旋转缓存
    this.cacheAutoRotate = this.config.autoRotate
    // 所有楼层数据
    this.floorsData = null

    this._init()
  }

  // 初始化组件
  _init() {
    // 初始化调试器
    this.debugger && this._initStats()
    // 初始化场景
    this._initScene()
    // 初始化相机
    this._initCamera()
    // 初始化渲染器
    this._initRenderer()
    // 初始化控制器
    this._initControl()
    // 初始化时钟
    this._initClock()
    // 初始化射线
    this._initRaycaster()
    // 初始化基准坐标系
    this.debugger && this._initAxes()

    // 初始化世界容器
    this.world = new THREE.Object3D()
    this.world.name = 'world'
    this.scene.add(this.world)

    // 初始化灯光
    this._initLight()
    // 初始化天空盒子
    this._initSkybox()
    // 初始化地面
    this._initGround()
    setTimeout(() => {
      // 初始化河流
      this._initRiver()
      // 初始化绿地
      this._initLanduse()
      // 初始化道路
      this._initRoad()
    }, 4000)
    // 初始化目标扩散组件
    this._initAimPulse()
    // 初始化护罩
    this._initShield()
    // 初始化楼层标题组件
    this._initStoreyTitle()
    // 初始化大楼模型
    this._initBuilding()

    // 解绑resize事件
    this._unbindEntityResizeHandler()
    // 绑定resize事件
    this._bindEntityResizeHandler()
    // 解绑点击事件
    this._unbindClickHandler()
    // 绑定点击事件
    this._bindClickHandler()
    // 解绑hover事件
    this._unbindHoverHandler()
    // 绑定hover事件
    this._bindHoverHandler()

    // 入场动画
    this._enterFly()
  }

  // 入场动画
  _enterFly() {
    let config = extend({}, this.config)

    let self = this
    let tl = new TimelineLite({
      paused: !1,
      delay: 0,
      repeat: 0,
      yoyo: true,
      onUpdate: function() {
        const _position = new THREE.Vector3(
          Math.sin(deg2Rad(tweenParam.pitch)) * tweenParam.height * Math.sin(deg2Rad(tweenParam.bearing)),
          Math.sin(deg2Rad(tweenParam.pitch)) * tweenParam.height * Math.cos(deg2Rad(tweenParam.bearing)),
          Math.cos(deg2Rad(tweenParam.pitch)) * tweenParam.height
        )
        self.camera.position.copy(_position)
      },
      onComplete: function() {
        self.control.enabled = true
        tl.timeline = null
        tl.onUpdate = null
        tl.onComplete = null
        tl = null
      }
    })

    let tweenParam = {
      bearing: config.dBearing,
      pitch: config.dPitch,
      height: config.dHeight
    }

    tl.to(
      tweenParam, 2, {
        bearing: config.bearing,
        pitch: config.pitch,
        height: config.height,
        ease: Linear.none
      }, 0)
  }

  // 初始化调试器
  _initStats() {
    const stats1 = new Stats()
    stats1.showPanel(0)
    stats1.domElement.style.cssText = 'position: absolute; top: 0px; left: 0px;'
    this.box.appendChild(stats1.domElement)

    const stats2 = new Stats()
    stats2.showPanel(1)
    stats2.domElement.style.cssText = 'position: absolute; top: 0px; left: 80px;'
    this.box.appendChild(stats2.domElement)

    const stats3 = new Stats()
    stats3.showPanel(2)
    stats3.domElement.style.cssText = 'position: absolute; top: 0px; left: 160px;'
    this.box.appendChild(stats3.domElement)
    this.stats = [stats1, stats2, stats3]
  }

  // 初始化时钟
  _initClock() {
    this.clock = new THREE.Clock()
  }

  // 初始化场景
  _initScene() {
    this.scene = new THREE.Scene()
  }

  // 初始化射线
  _initRaycaster() {
    this.raycaster = new THREE.Raycaster()
  }

  // 初始化相机
  _initCamera() {
    let config = this.config
    this.camera = new THREE.PerspectiveCamera(45, this.box.clientWidth / this.box.clientHeight, 1, EARTH_RADIUS * 250)
    this.camera.up.set(0, 0, 1)
    this.camera.position.set(
      Math.sin(deg2Rad(config.dPitch)) * config.dHeight * Math.sin(deg2Rad(config.dBearing)),
      Math.sin(deg2Rad(config.dPitch)) * config.dHeight * Math.cos(deg2Rad(config.dBearing)),
      Math.cos(deg2Rad(config.dPitch)) * config.dHeight
    )
  }

  // 初始化渲染器
  _initRenderer() {
    this.renderer = new THREE.WebGLRenderer({
      antialias: true,
      alpha: false
    })
    this.renderer.setSize(this.box.clientWidth, this.box.clientHeight)

    const pixelRatio = window.devicePixelRatio < 1.5 ? 1.5 : window.devicePixelRatio
    this.renderer.setPixelRatio(pixelRatio)

    this.renderer.autoClear = true
    this.renderer.shadowMap.enabled = true
    this.renderer.setClearColor(new THREE.Color('#000000'), 1)
    this.box.appendChild(this.renderer.domElement)
  }

  // 初始化场景控制器
  _initControl() {
    this.control = new EarthControl(this.camera, this.box, function() {}, { longitude: this.center[0], latitude: this.center[1] })
    
    this.control.enabled = false
    this.control.enableDamping = true
    this.control.dampingFactor = 0.075
    this.control.enableZoom = true
    this.control.autoRotate = this.config.autoRotate
    this.control.autoRotateSpeed = 0.03
    this.control.maxDistance = this.config.maxDistance
    this.control.minDistance = this.config.minDistance
    this.control.rotateSpeed = 0.08
  }

  // 初始化场景灯光
  _initLight() {
    this.ambientLight = new THREE.AmbientLight(new THREE.Color('#ffffff'), 0.5)

    this.ambientLight.name = 'base_ambientLight'
    this.scene.add(this.ambientLight)

    let light1 = new THREE.DirectionalLight(new THREE.Color('#ffffff'), 0.75, 0)
    light1.position.set(-1, -1, 1).normalize()
    this.directionalLights.push(light1)
    this.scene.add(light1)

    let light2 = new THREE.DirectionalLight(new THREE.Color('#ffffff'), 0.25, 0)
    light2.position.set(-1, 1, 1).normalize()
    this.directionalLights.push(light2)
    this.scene.add(light2)

    let light3 = new THREE.DirectionalLight(new THREE.Color('#ffffff'), 0.25, 0)
    light3.position.set(1, -1, 1).normalize()
    this.directionalLights.push(light3)
    this.scene.add(light3)

    let light4 = new THREE.DirectionalLight(new THREE.Color('#ffffff'), 0.25, 0)
    light4.position.set(1, 1, 1).normalize()
    this.directionalLights.push(light4)
    this.scene.add(light4)

    // let light5 = new THREE.SpotLight(new THREE.Color('#94F6FA'))
    // light5.position.set(0, 0, 450)
    // this.scene.add(light5)

    let light5 = new THREE.DirectionalLight(new THREE.Color('#94F6FA'), 0.75, 0)
    light5.position.set(0, 0, 1).normalize()
    this.directionalLights.push(light5)
    this.scene.add(light5)
  }

  // 初始化坐标系
  _initAxes() {
    this.axes = new THREE.AxesHelper(EARTH_RADIUS * 2)
    this.axes.name = 'axes'
    this.scene.add(this.axes)
  }

  // 绑定resize事件
  _bindEntityResizeHandler() {
    window.addEventListener('resize', this._resize.bind(this), false)
  }

  // 解绑resize事件
  _unbindEntityResizeHandler() {
    window.removeEventListener('resize', this._resize.bind(this), false)
  }

  // 解绑点击事件
  _unbindClickHandler() {
    this.box.removeEventListener('click', this._click.bind(this), false)
  }

  // 绑定点击事件
  _bindClickHandler() {
    this.box.addEventListener('click', this._click.bind(this), false)
  }

  // 解绑hover事件
  _unbindHoverHandler() {
    this.box.removeEventListener('mousemove', this._hover.bind(this), false)
  }

  // 绑定hover事件
  _bindHoverHandler() {
    this.box.addEventListener('mousemove', this._hover.bind(this), false)
  }

  // resize回调
  _resize() {
    this.renderer.setSize(this.box.clientWidth, this.box.clientHeight)
    this.camera.aspect = this.box.clientWidth / this.box.clientHeight
    this.camera.updateProjectionMatrix()
    this.renderer.render(this.scene, this.camera)
  }

  // click事件回调
  _click(e) {
    e.preventDefault()

    const boundingClientRect = this.box.getBoundingClientRect()
    this.mousePos.set(((e.clientX - boundingClientRect.left) / this.box.offsetWidth) * 2 - 1, -((e.clientY - boundingClientRect.top) / this.box.offsetHeight) * 2 + 1)
    this.raycaster.setFromCamera(this.mousePos, this.camera)

    let allSprites = []
    this.scene.traverse(e => {
      if (e.click) {
        allSprites.push(e)
      }
    })

    // 获取当前射线上所有满足要求的对象
    const intersects = this.raycaster.intersectObjects(allSprites)

    // 如果没有一个对象满足要求,也就是没有点击到任何对象时.
    if (intersects.length === 0) {
      for (let j = 0, len2 = this.storeys.length; j < len2; j++) {
        let storey = this.storeys[j]
        storey.destroy()
      }

      let keys = Object.keys(this.buildings)
      for (let j = 0, len2 = keys.length; j < len2; j++) {
        let building = this.buildings[keys[j]]
        building.unChoose()
      }
      this.enableHover = false
      this.storeys = []

      if (this.shield.entity.scale.x > 0.1) this.shield.hide()
      if (this.aimPulse.entity.scale.x > 0.1) this.aimPulse.hide()
      this.storeyTitle.hide()

      this.control.autoRotate = this.cacheAutoRotate
      this.storeyUnChooseCb()
    }
    // 如果有点击到对象
    for (let i = 0; i < intersects.length; i++) {
      if (intersects[i].object && intersects[i].object.click && intersects[i].object.visible) {
        // 执行楼层取消选中回调
        if (intersects[i].object.name.indexOf('storey') === -1) {
          this.storeyUnChooseCb()
        }
        // 执行楼点击事件
        intersects[i].object.userData.screenPos = { x: e.clientX, y: e.clientY }
        intersects[i].object.click(intersects[i].object)
        break
      }
    }

    if (!this.debugger) return
    // 调试模式下执行, 用于点击获取当前点击位置的经纬度坐标功能.

    const fxl = new THREE.Vector3(0, 0, 1)
    const groundPlane = new THREE.Plane(fxl, 0)

    const _raycaster = new THREE.Raycaster()
    _raycaster.setFromCamera(this.mousePos, this.camera)
    const ray = _raycaster.ray
    const intersect = ray.intersectPlane(groundPlane)

    if (!intersect) return false
    let lon = this.control.getLongitude()
    let lat = this.control.getLatitude()
    const _format = unTransformCoordinate(lngLatToWorld([lon, lat]), [intersect.x, intersect.y])
    const lngLat = worldToLngLat(_format)
    this.debugger && console.log('当前点击的经纬度为: ', lngLat)
  }

  // hover事件回调
  _hover(e) {
    e.preventDefault()

    const boundingClientRect = this.box.getBoundingClientRect()
    this.mousePos.set(((e.clientX - boundingClientRect.left) / this.box.offsetWidth) * 2 - 1, -((e.clientY - boundingClientRect.top) / this.box.offsetHeight) * 2 + 1)
    this.raycaster.setFromCamera(this.mousePos, this.camera)

    const allSprites = []
    const allClickSprites = []
    this.scene.traverse(e => {
      if (e.hover) {
        allSprites.push(e)
      }
      if (e.click) {
        allClickSprites.push(e)
      }
    })

    // 获取当前所有满足要求的对象集
    const clickIntersects = this.raycaster.intersectObjects(allClickSprites)
    // 动态更改鼠标样式
    this.box.style.cursor = clickIntersects.length > 0 ? 'pointer' : 'default'

    // 如果不支持hover事件则不执行接下去的逻辑
    if (!this.enableHover) return false
    // 如果没有hover到任何对象上则不往下执行
    if (allSprites.length === 0) return false
    const intersects = this.raycaster.intersectObjects(allSprites)
    // 如果没有hover到任何楼层对象上
    if (intersects.length === 0) {
      let style = this.style.building
      // 重新设置样式
      for (let i = 0, len = this.storeys.length; i < len; i++) {
        let status = this.floorsData[i + 1] ? this.floorsData[i + 1].status : 'normal'
        this.storeys[i].entity.material.color = new THREE.Color(style[status].color)
      }
      // 执行取消选中回调
      this.storeyUnChooseCb && isFunction(this.storeyUnChooseCb) && this.storeyUnChooseCb()
    }
    // 如果有满足要求的对象
    for (let i = 0; i < intersects.length; i++) {
      // 则取最前面的对象执行hover事件
      if (intersects[i].object && intersects[i].object.hover) {
        intersects[i].object.userData.screenPos = { x: e.clientX, y: e.clientY }
        if (!this._hoverEntity) {
          this.control.autoRotate = false
          intersects[i].object.hover(intersects[i].object)
          this._hoverEntity = intersects[i].object
        } else {
          if (intersects[i].object.uuid !== this._hoverEntity.uuid || this._hoverEntity === null) {
            this.control.autoRotate = false
            intersects[i].object.hover(intersects[i].object)
            this._hoverEntity = intersects[i].object
          }
        }
        break
      }
    }
  }

  // 初始化天空盒子
  _initSkybox() {
    let style = extend({}, this.style.skybox)
    this.skybox = new Skybox(this, {
      map: style.url,
      color: style.color
    })
  }

  // 初始化地面
  _initGround() {
    let style = extend({}, this.style.ground)
    this.ground = new Ground(this, {
      color: style.color,
      opacity: style.opacity,
      renderOrder: 1
    })
  }

  // 初始化河流
  _initRiver() {
    let style = extend({}, this.style.river)
    axios.get(style.url).then(rest => {
      const { features } = rest.data
      let filterData = features.filter(function(feature) {
        return feature.geometry !== null
      })
  
      let river = new River(this, {
        path: filterData,
        color: style.color,
        opacity: style.opacity,
        repeat: 20
      })
      this.rivers.push(river)
    })

    axios.get('/json/water-test.json').then(rest => {
      const { features } = rest.data
      let filterData = features.filter(function(feature) {
        return feature.geometry !== null
      })
  
      let river = new River(this, {
        path: filterData,
        color: style.color,
        opacity: style.opacity,
        repeat: 20
      })
      this.rivers.push(river)
    })
  }

  // 初始化绿地
  _initLanduse() {
    let style = extend({}, this.style.landuse)
    axios.get(style.url).then(rest => {
      const { features } = rest.data
      let filterData = features.filter(function(feature) {
        return feature.geometry !== null
      })

      this.landuse = new Landuse(this, {
        path: filterData,
        color: style.color,
        opacity: style.opacity,
        useMap: false,
        renderOrder: 5
      })
    })
  }

  // 初始化道路
  _initRoad() {
    let style = extend({}, this.style.road)
    axios.get(style.url).then(rest => {
      const { features } = rest.data
      let filterData = features.filter(function(feature) {
        return feature.geometry !== null
      })

      let num = 1500
      let perRoads = Math.ceil(filterData.length / num)

      for (let i = 0; i < perRoads; i++) {
        let road = new Road(this, {
          lineWidth: style.lineWidth,
          renderOrder: 12,
          opacity: style.opacity,
          color: style.color,
          isAdaption: false,
          path: i === perRoads - 1 ? filterData.slice(i * num, filterData.length - 1) : filterData.slice(i * num, (i + 1) * num)
        })
        this.roads.push(road)
      }
    })
  }

  // 初始化目标选中扩散组件
  _initAimPulse() {
    let style = extend({}, this.style.aimPulse)
    this.aimPulse = new AimPulse(this, {
      color: style.color,
      renderOrder: 15,
      opacity: style.opacity,
      radius: style.radius
    })
  }

  // 初始化楼层标题文本组件
  _initStoreyTitle() {
    let style = extend({}, this.style.storeyTitle)
    this.storeyTitle = new Text(this, {
      opacity: style.opacity,
      renderOrder: 30,
      height: style.height,
      color: style.color,
      text: '实验楼0楼',
      background: true,
      backgroundColor: style.backgroundColor,
      sizeAttenuation: false
    })
  }

  // 初始化楼层展示外接护罩
  _initShield() {
    let style = extend({}, this.style.shield)
    this.shield = new Shield(this, {
      color: style.color,
      opacity: style.opacity,
      renderOrder: 30,
      radius: style.radius
    })
  }

  // 初始化大楼模型数据
  _initBuilding() {
    axios.get('/json/building.json').then(rest => {
      const { features } = rest.data
      for (let i = 0, len = features.length; i < len; i++) {
        let feature = features[i]
        let coordinates = feature.geometry.coordinates[0]
        let properties = feature.properties
        let path = []

        for (let j = 0, len2 = coordinates.length; j < len2; j++) {
          let coordinate = coordinates[j]

          let world = lngLatToWorld(coordinate)
          let format = transformCoordinate(this.gisCenter, world)
          path.push(new THREE.Vector2(format.x, format.y))
        }

        const options = {
          path: path,
          floor: properties.floor,
          color: '#1e60eb',
          renderOrder: 10,
          opacity: 0.9,
          bevelThickness: 1,
          userData: {
            properties: properties,
            path: path
          }
        }

        if (this.debugger) {
          options.click = function(data) {
            console.log(data.userData.properties.id)
          }
        }

        let building = new Building(this, options)
        this.buildings[properties.id] = building
      }
    })
  }

  // 大楼点击事件回调
  _buildingClickFn(building) {
    let floor = building.userData.properties.floor
    let path = building.userData.path
    let id = building.userData.properties.id

    let keys = Object.keys(this.buildings)
    for (let i = 0, len2 = keys.length; i < len2; i++) {
      let building = this.buildings[keys[i]]
      building.unChoose()
    }

    this._removeStoreys()
    if (this.shield.entity.scale.x > 0.1) this.shield.hide()
    this.storeyTitle.hide()

    building.ctx.choose()

    // 创建建筑底部动画光圈
    let boundRect = getBoundRect(path)
    let centerPos = new THREE.Vector2((boundRect.minX + boundRect.maxX) / 2, (boundRect.minY + boundRect.maxY) / 2)
    let radius = centerPos.distanceTo(new THREE.Vector2(boundRect.minX, boundRect.minY)) * 1.2
    let buildingData = this._getBuildingDataByModelId(id)

    this.aimPulse.setPosition(centerPos, 0.5)
    this.aimPulse.show(this._initStorey.bind(this), floor, path, centerPos, radius, buildingData)
  }

  // 气泡点击事件回调
  _markerClickFn(marker) {
    this.debugger && console.log(marker)
    let building = marker.building.entity
    let floor = building.userData.properties.floor
    let path = building.userData.path
    let id = building.userData.properties.id

    let keys = Object.keys(this.buildings)
    for (let i = 0, len2 = keys.length; i < len2; i++) {
      let build = this.buildings[keys[i]]
      build.unChoose()
    }

    this._removeStoreys()
    if (this.shield.entity.scale.x > 0.1) this.shield.hide()
    this.storeyTitle.hide()

    building.ctx.choose()

    // 创建建筑底部动画光圈
    let boundRect = getBoundRect(path)
    let centerPos = new THREE.Vector2((boundRect.minX + boundRect.maxX) / 2, (boundRect.minY + boundRect.maxY) / 2)
    let radius = centerPos.distanceTo(new THREE.Vector2(boundRect.minX, boundRect.minY)) * 1.2
    let buildingData = this._getBuildingDataByModelId(id)

    this.popupClickCb && isFunction(this.popupClickCb) && this.popupClickCb(buildingData)

    this.aimPulse.setPosition(centerPos, 0.5)
    this.aimPulse.show(this._initStorey.bind(this), floor, path, centerPos, radius, buildingData)
  }

  // 设置当前楼所有楼层数据
  setFloorsData(data) {
    setTimeout(() => {
      this.floorsData = data
      let keys = Object.keys(data)
      let style = this.style.building
      for (let i = 0; i < keys.length; i++) {
        let key = keys[i]
        let _data = data[key]
        let status = _data.status ? _data.status : 'normal'
        this.storeys[key - 1].entity.material.color = new THREE.Color(style[status].color)
      }
    }, 800)
  }

  // 根据大楼模型id获取对应的大楼数据
  _getBuildingDataByModelId(modelId) {
    let data = null
    let keys = Object.keys(keymap)
    for (let i = 0; i < keys.length; i++) {
      if (keymap[keys[i]].modelId === +modelId) {
        data = keymap[keys[i]]
        break
      }
    }
    return data
  }

  // 初始化楼层
  _initStorey(floor, path, centerPos, radius, buildingData) {
    this._removeStoreys()
    let centerHeight = (floor / 2) * ELEVATION * 1.5
    this.shield.setPosition(centerPos, 150 + centerHeight)
    let shieldRadius = radius > centerHeight * 1.2 ? radius : centerHeight * 1.2
    this.shield.setRadius(shieldRadius)
    this.shield.show(function() {})

    this.storeyTitle.setPosition(centerPos, 150 + centerHeight + shieldRadius + 10)
    this.storeyTitle.setText(buildingData.buildingName)
    this.storeyTitle.show()

    let self = this
    let tl = new TimelineLite({
      paused: !1,
      delay: 0,
      repeat: 0,
      yoyo: true,
      onComplete: function() {
        self.enableHover = true
      }
    })

    for (let i = 0; i < floor; i++) {
      let storey = new Building(this, {
        name: 'storey',
        path: path,
        floor: 1,
        color: '#1e60eb',
        renderOrder: 10,
        userData: {
          floorNum: i + 1,
          buildingData: buildingData
        },
        hover: this._storeyHoverFn.bind(this)
      })

      this.storeys.push(storey)

      let height = 150 + i * ELEVATION * 1.5
      tl.to(
        storey.entity.position,
        0.5,
        {
          z: height,
          ease: Linear.none
        },
        0
      )
    }
  }

  // 楼层hover事件回调
  _storeyHoverFn(storey) {
    let style = this.style.building
    for (let i = 0, len = this.storeys.length; i < len; i++) {
      let status = this.floorsData[i + 1] ? this.floorsData[i + 1].status : 'normal'
      this.storeys[i].entity.material.color = new THREE.Color(style[status].color)
    }
    storey.material.color = new THREE.Color('#27C0ED')

    if (this.storeyChooseCb && isFunction(this.storeyChooseCb)) {
      let data = this.floorsData[storey.userData.floorNum]

      if (data) {
        let _data = Object.assign(data, storey.userData)
        this.storeyChooseCb(_data)
      } else {
        this.storeyUnChooseCb()
      }
    }
  }

  // 清楚当前楼层数据
  _removeStoreys() {
    for (let i = 0, len = this.storeys.length; i < len; i++) {
      let storey = this.storeys[i]
      storey.destroy()
    }
    this.enableHover = false
    this.storeys = []
  }

  // 主动画函数
  _render() {
    if (this.debugger) {
      this.stats[0].update()
      this.stats[1].update()
      this.stats[2].update()
    }

    const delta = this.clock.getDelta()
    this.control.update(delta)

    const lon = this.control.getLongitude()
    const lat = this.control.getLatitude()
    const world = lngLatToWorld([lon, lat])
    const format = transformCoordinate(this.gisCenter, world)
    this.world.position.set(-format.x, -format.y, 0)

    this.renderer.render(this.scene, this.camera)
  }

  // 销毁three对象用相关函数
  _disposeHierarchy(node, callback) {
    for (var i = node.children.length - 1; i >= 0; i--) {
      var child = node.children[i]
      this._disposeHierarchy(child, callback)
      callback(child)
    }
  }

  // 销毁three对象用相关函数
  _dispose(e) {
    if (e.geometry) {
      e.geometry.dispose()
    }
    if (e.material) {
      if (e.material instanceof THREE.MeshFaceMaterial || e.material.type === 'MeshFaceMaterial') {
        e.material.materials.forEach(material => {
          if (material.map) material.map.dispose()
          if (material.lightMap) material.lightMap.dispose()
          if (material.bumpMap) material.bumpMap.dispose()
          if (material.normalMap) material.normalMap.dispose()
          if (material.specularMap) material.specularMap.dispose()
          if (material.envMap) material.envMap.dispose()
          material.dispose()
        })
      } else {
        if (e.material.map) e.material.map.dispose()
        if (e.material.lightMap) e.material.lightMap.dispose()
        if (e.material.bumpMap) e.material.bumpMap.dispose()
        if (e.material.normalMap) e.material.normalMap.dispose()
        if (e.material.specularMap) e.material.specularMap.dispose()
        if (e.material.envMap) e.material.envMap.dispose()
        e.material.dispose()
      }
    }
    if (e.dispose) {
      e.dispose()
    }
    e.parent.remove(e)
  }

  // 更新气泡数据
  updateData(data) {
    if (Object.keys(this.markers).length === 0) {
      this._initMarker(data)
    } else {
      this._updateMarker(data)
    }

    for (let i = 0; i < data.length; i++) {
      let buildingId = data[i].buildingId
      if (this._getModelId(buildingId)) {
        let modelId = this._getModelId(buildingId)
        this.buildings[modelId].setStatus(data[i].status)
      }
    }
  }

  // 根据buildingId获取对应的modelid
  _getModelId(buildingId) {
    return keymap[buildingId] && keymap[buildingId].modelId
  }

  // 根据modelId获取对应的buildingId
  _getBuildingId(modelId) {
    let id = null
    let keys = Object.keys(keymap)
    for (let i = 0; i < keys.length; i++) {
      if (keymap[keys[i]].modelId === +modelId) {
        id = keys[i]
        break
      }
    }
    return id
  }

  // 初始化气泡
  _initMarker(data) {
    this.debugger && console.log('initMarker', data)
    for (let i = 0; i < data.length; i++) {
      let modelId = this._getModelId(data[i].buildingId)

      if (!this.buildings[modelId]) {
        this.debugger && console.log(data[i].buildingId)
      } else {
        let marker = new Marker(this, {
          data: data[i],
          renderOrder: 100,
          building: this.buildings[modelId],
          click: this._markerClickFn.bind(this)
        })
        this.markers[data[i].buildingId] = marker
      }
    }
  }

  // 更新气泡数据
  _updateMarker(data) {
    this.debugger && console.log('updateMarker', data)
    for (let i = 0; i < data.length; i++) {
      let id = data[i].buildingId
      let modelId = this._getModelId(id)

      if (!this.markers[id]) {
        if (!this.buildings[modelId]) {
          this.debugger && console.log(data[i].buildingId)
        } else {
          let marker = new Marker(this, {
            data: data[i],
            renderOrder: 100
          })
          this.markers[data[i].buildingId] = marker
        }
      } else {
        this.markers[id].updateData(data[i])
      }
    }
  }

  // 筛选气泡数据
  filter(ids) {
    let markerKeys = Object.keys(this.markers)
    for (let i = 0; i < markerKeys.length; i++) {
      if (ids.indexOf(+markerKeys[i]) > -1) {
        this.markers[markerKeys[i]].setVisible(true)
      } else {
        this.markers[markerKeys[i]].setVisible(false)
      }
    }
  }

  // 清空筛选条件
  filterReset() {
    let markerKeys = Object.keys(this.markers)
    for (let i = 0; i < markerKeys.length; i++) {
      this.markers[markerKeys[i]].setVisible(true)
    }
  }

  // 切换场景自动选装状态
  toggleRotate() {
    this.control.autoRotate = !this.control.autoRotate
    this.cacheAutoRotate = this.control.autoRotatee
  }

  // 场景跳转函数
  flyTo(options) {
    const self = this
    self.control.enabled = false
    const {lngLat, height = 800, duration = 3, maxHeight = 40000, pitch = 75} = options

    const _lng = this.control.getLongitude()
    const _lat = this.control.getLatitude()
    const _height = new THREE.Vector3().copy(this.control.object.position).sub(this.control.target).length()

    const cameraPos = self.camera.position
    let pos1 = new THREE.Vector2(cameraPos.x, cameraPos.y)
    let len1 = pos1.length()
    let asin = Math.asin(pos1.y / len1)
    let acos = Math.acos(pos1.x / len1)
    let bearing = acos < Math.PI / 2 ? Math.PI / 2 - asin : Math.PI * 3 / 2 + asin
    let _pitch = Math.asin(len1 / cameraPos.length())

    let params = {
      ratio: 0,
      pitch: _pitch
    }
    let tl = new TimelineLite({
      paused: !1,
      delay: 0,
      repeat: 0,
      yoyo: true,
      onUpdate: function() {
        let resultLng = mix(_lng, lngLat[0], params.ratio)
        let resultLat = mix(_lat, lngLat[1], params.ratio)
        self.control.setLongitude(resultLng)
        self.control.setLatitude(resultLat)

        let resultHeight = 0
        if (params.ratio < 0.5) {
          resultHeight = mix(_height, maxHeight, params.ratio / 0.5)
        } else {
          resultHeight = mix(maxHeight, height, (params.ratio - 0.5) / 0.5)
        }
        self.camera.position.set(
          Math.sin(params.pitch) * resultHeight * Math.sin(bearing),
          Math.sin(params.pitch) * resultHeight * Math.cos(bearing),
          Math.cos(params.pitch) * resultHeight
        )
      },
      onComplete: function() {
        self.control.enabled = true
        tl.timeline = null
        tl.onUpdate = null
        tl.onComplete = null
        tl = null
      }
    })

    tl.to(params, duration, {
      ratio: 1,
      pitch: Math.PI / 180 * pitch,
      ease: Power4.easeInOut
    })
  }

  // 组件销毁
  destroy() {
    this.skybox && this.skybox.destroy()
    this.ground && this.ground.destroy()
    this.landuse && this.landuse.destroy()
    this.aimPulse && this.aimPulse.destroy()
    this.shield && this.shield.destroy()
    this.storeyTitle && this.storeyTitle.destroy()

    for (let i = 0; i < this.rivers.length; i++) {
      this.rivers[i] && this.rivers[i].destroy()
    }

    for (let i = 0; i < Object.keys(this.buildings); i++) {
      this.buildings[Object.keys(this.buildings)[i]] && this.buildings[Object.keys(this.buildings)[i]].destroy()
    }

    for (let i = 0; i < this.roads.length; i++) {
      this.roads[i] && this.roads[i].destroy()
    }

    for (let i = 0; i < this.storeys.length; i++) {
      this.storeys[i] && this.storeys[i].destroy()
    }

    for (let i = 0; i < Object.keys(this.markers); i++) {
      this.markers[Object.keys(this.markers)[i]] && this.markers[Object.keys(this.markers)[i]].destroy()
    }
    this.animationManager.unload('render')
  }
}
